<?php
/**
 * 绮梦PHP框架 (Dream PHP Freamework)
 * 免费 简单 快速 高效 安全 专业
 * 绮梦PHP框架是一个基于组件的PHP框架，用于快速开发现代Web应用程序。
 *
 * @author lewis (lewisld@163.com)
 * @copyright Copyright (c) 2015, lewis (http://www.dreamfreamework.com/)
 * @license http://www.dreamfreamework.com/licenses/ Apache License
 * @link http://www.dreamfreamework.com/
 */

namespace System\Caching;

use System\Caching\Exception\ExpiryException;
use System\Caching\Exception\OptionSetException;
use System\Caching\Exception\ClearException;
use System\Caching\Exception\DropException;

/**
 * 缓存驱动适配器抽象类
 *
 * @package Dream
 * @subpackage AbstractAdapter
 * @namespace System\Caching
 * @author lewis (lewisld@yeah.net)
 */
abstract class AbstractAdapter
{

    /**
     * 缓存过期时间（秒）
     *
     * @var int
     */
    protected $expiry = 3600; // 3600秒

    /**
     * 缓存前缀
     *
     * @var string
     */
    protected $prefix = '';

    /**
     * 是否序列化
     *
     * @var bool
     */
    protected $serialize = true;

    /**
     * 设置缓存驱动适配器配置
     *
     * @param string $key 键名
     * @param string $value 值
     * @throws System\Caching\Exception\CacheExpiryException 过期时间异常
     * @throws System\Caching\Exception\CacheOptionSetException 配置选项异常
     * @return bool 设置成功就返回“true”
     */
    public function setOption($key, $value)
    {
        switch ($key) {
            case 'expiry': // 过期时间
                $value = $this->expiryStringToInt($value);
                if ($value < 1) {
                    throw new ExpiryException([
                        'system.cache.expiry.set.overrange'
                    ]);
                }
                $this->expiry = $value;
                break;
            case 'prefix': // 前缀
                $this->prefix = (string) $value;
                break;
            case 'serialize': // 序列化
                $this->serialize = (bool) $value;
                break;
            default:
                throw new OptionSetException([
                    [
                        'system.cache.option.set.void'
                    ],
                    [
                        'key' => $key
                    ]
                ]);
        }
        return true;
    }

    /**
     * 缓存过期时间的字符串转换为秒 ('59m60s' => 3600)
     *
     * @param string $string 缓存失效时间的字符串
     * @return int 返回缓存失效时间的秒数
     */
    protected function expiryStringToInt($string)
    {
        $string = preg_replace("/[^0-9A-Za-z]/", null, $string);
        $string = strtolower($string);
        preg_match_all("/(\\d+)([w|d|h|m|s])/", $string, $match);
        $times = $match[1];
        $delimiters = $match[2];
        $expiry = 0;
        foreach ($delimiters as $key => $delimiter) {
            switch ($delimiter) {
                case 'w': // 一星期
                    $multiplier = 60 * 60 * 24 * 7;
                    break;
                case 'd': // 天
                    $multiplier = 60 * 60 * 24;
                    break;
                case 'h': // 时
                    $multiplier = 60 * 60;
                    break;
                case 'm': // 分
                    $multiplier = 60;
                    break;
                case 's': // 秒
                    $multiplier = 1;
                    break;
                default:
                    $multiplier = 1;
                    break;
            }
            $expiry += (int) $times[$key] * $multiplier;
        }
        return $expiry;
    }

    /**
     * {@inheritdoc }
     */
    public function clear()
    {
        throw new ClearException('not ready yet');
    }

    /**
     * {@inheritdoc }
     */
    public function drop()
    {
        throw new DropException('not ready yet');
    }

    /**
     *
     * @param string $key
     * @return string
     */
    protected function getKey($key)
    {
        // return md5($key);
        return $key;
    }

    /**
     * Builds the key according to the prefix and other options
     *
     * @param string key
     * @return string
     */
    protected function buildKey($key)
    {
        return $this->prefix . $key;
    }

    /**
     * Packages the data to be stored by the internal caching driver
     * according to the options on the adapter.
     *
     * @param mixed $data
     * @return mixed
     */
    protected function packData($data)
    {
        if ($this->serialize) {
            return $this->serialize($data);
        }

        return $data;
    }

    /**
     * Unpackages the data retrieved by the internal caching driver
     * according to the options on the adapter.
     * This will be the inverse
     * of packData IF the options are set correctly.
     *
     * @param mixed $data
     * @return mixed
     */
    protected function unpackData($data)
    {
        if ($this->serialize) {
            return $this->unserialize($data);
        }

        return $data;
    }

    protected function serialize($value)
    {
        return serialize($value);
    }

    protected function unserialize($value)
    {
        return unserialize($value);
    }
}